package http

import (
	"context"
	"gitee.com/sdynasty/yogurt/middleware"
	"gitee.com/sdynasty/yogurt/transport"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/mux"
	"net/http"
	"net/url"
	"time"
)

var _ Context = (*wrapper)(nil)

type Context interface {
	context.Context
	Reset(*gin.Context)
	Vars() url.Values
	Query() url.Values
	Form() url.Values
	Header() http.Header
	Request() *http.Request
	Response() http.ResponseWriter
	Middleware(middleware.Handler) middleware.Handler
	Bind(any) error
}

type responseWriter struct {
	code int
	w    http.ResponseWriter
}

func (w *responseWriter) reset(res http.ResponseWriter) {
	w.w = res
	w.code = http.StatusOK
}

type wrapper struct {
	srv *Server
	g   *gin.Context
	req *http.Request
	res http.ResponseWriter
	w   responseWriter
}

func (c *wrapper) Reset(g *gin.Context) {
	req, res := g.Request, g.Writer
	c.g = g
	c.req = req
	c.res = res
	c.w.reset(res)
}
func (c *wrapper) Header() http.Header {
	return c.req.Header
}

func (c *wrapper) Vars() url.Values {
	raws := mux.Vars(c.req)
	vars := make(url.Values, len(raws))
	for k, v := range raws {
		vars[k] = []string{v}
	}
	return vars
}

func (c *wrapper) Form() url.Values {
	if err := c.req.ParseForm(); err != nil {
		return url.Values{}
	}
	return c.req.Form
}

func (c *wrapper) Query() url.Values {
	return c.req.URL.Query()
}
func (c *wrapper) Request() *http.Request        { return c.req }
func (c *wrapper) Response() http.ResponseWriter { return c.res }

func (c *wrapper) Middleware(h middleware.Handler) middleware.Handler {
	var operation string
	if tr, ok := transport.FromServerContext(c.req.Context()); ok {
		operation = tr.Operation()
	} else {
		operation = c.req.URL.Path
	}
	return middleware.Chain(c.srv.middleware.Match(operation)...)(h)
}

func (c *wrapper) Bind(v any) error {
	return c.srv.decBody(c.req, v)
}

func (c *wrapper) Deadline() (time.Time, bool) {
	if c.req == nil {
		return time.Time{}, false
	}
	return c.req.Context().Deadline()
}

func (c *wrapper) Done() <-chan struct{} {
	if c.req == nil {
		return nil
	}
	return c.req.Context().Done()
}

func (c *wrapper) Err() error {
	if c.req == nil {
		return context.Canceled
	}
	return c.req.Context().Err()
}

func (c *wrapper) Value(key any) any {
	if c.req == nil {
		return nil
	}
	return c.req.Context().Value(key)
}
